﻿
using System;
using System.Drawing;
using System.Collections.Generic;

using GECS.Object;
using GECS.MapEngine;

namespace GECS.Collision
{
	/// <summary>
	/// A Grid implementation to resolve collisions on this map
	/// </summary>
	public class Grid : ICollisionEngine
	{
		
		// our grid of lists
		private List<GObject>[,] grid;		
		private int rows, cols;
		
		private List<GObject> retrieveList = new List<GObject>();
		
		/// <summary>
		/// Constructs a Grid to resolve collisions
		/// </summary>
		public Grid()
		{
			SetBounds(0, 0, Map.Width, Map.Height);
		}
		
		/// <summary>
		/// Clear the objects in the engine. (Doesn't clear the MAP)
		/// </summary>
		public void Clear()
		{
			for (int x=0; x<cols; x++)
			{
				for (int y=0; y<rows; y++)
				{
					grid[x, y].Clear();
				}
			}
		}
		
		/// <summary>
		/// Inserts a GObject in to the engine.
		/// </summary>
		/// <param name="obj">The object to be added.</param>
		public void Insert(GObject obj)
		{
			int topLeftX = Math.Max(0, (int) obj.X / 64);
			int topLeftY = Math.Max(0, (int) obj.Y / 64);
			int bottomRightX = Math.Min(cols-1, (int) (obj.X + obj.Width -1) / 64);
			int bottomRightY = Math.Min(rows-1, (int) (obj.Y + obj.Height -1) / 64);
			for (int x = topLeftX; x <= bottomRightX; x++)
			{
				for (int y = topLeftY; y <= bottomRightY; y++)
				{
					grid[x, y].Add(obj);
				}
			}
		}
		
		/// <summary>
		/// Inserts a List of GObject's to the engine.
		/// </summary>
		/// <param name="objs">The list of objects.</param>
		public void Insert(List<GObject> objs)
		{
			foreach (GObject obj in objs)
			{
				Insert(obj);
			}
		}
		
		/// <summary>
		/// Returns a list of objects that are likely collidable.
		/// </summary>
		/// <param name="obj">The object to be tested.</param>
		public List<GObject> Retrieve(GObject obj)
		{
			return Retrieve(obj.Bounds);
		}
		
		/// <summary>
		/// Returns a list of objects that are likely to be present
		/// in a rectangle.
		/// </summary>
		/// <param name="rect">The rectangle to be tested.</param>
		public List<GObject> Retrieve(Rectangle rect)
		{
			retrieveList.Clear();
			int topLeftX = Math.Max(0, (int) rect.X / 64);
			int topLeftY = Math.Max(0, (int) rect.Y / 64);
			int bottomRightX = Math.Min(cols-1, (int) (rect.X + rect.Width -1) / 64);
			int bottomRightY = Math.Min(rows-1, (int) (rect.Y + rect.Height -1) / 64);
			for (int x = topLeftX; x <= bottomRightX; x++)
			{
				for (int y = topLeftY; y <= bottomRightY; y++)
				{
					List<GObject> cell = grid[x, y];
					foreach (GObject o in cell)
					{
						if (!retrieveList.Contains(o))
							retrieveList.Add(o);
					}
    			}
			}
			return retrieveList;
		}
		
		/// <summary>
		/// Sets the bounds of the visible area of the map.
		/// </summary>
		/// <param name="x_pos">The x-coordinate of the top left point</param>
		/// <param name="y_pos">The y-coordinate of the top left point</param>
		/// <param name="width">The width of the visible area</param>
		/// <param name="height">The height of the visible area</param>
		public void SetBounds(int x_pos, int y_pos, int width, int height)
		{
			rows = (Map.Height + 63) / 64;
			cols = (Map.Width + 63) / 64;
			grid = new List<GObject>[cols, rows];
			for (int x=0; x<cols; x++)
			{
				for (int y=0; y<rows; y++)
				{
					grid[x, y] = new List<GObject>();
				}
			}
		}
		
	}
}
